using NFreeMarker.Template;
using NUnit.Framework;

// ReSharper disable InconsistentNaming
namespace NFreeMarker.Tests.Template
{
    public class TemplateTests_ProcessBuiltInsForStrings
    {
        [TestFixture]
        public class When_processing_a_cap_first_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_first_letter_is_changed_to_upper_case()
            {
                SetTemplateContent("${message?cap_first}");
                AddTemplateVariable("message", "   hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("   Hello world"));
            }
        }

        [TestFixture]
        public class When_processing_a_capitalize_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_first_letter_in_a_word_is_upper_case_and_the_other_are_lower_case()
            {
                SetTemplateContent("${message?capitalize}");
                AddTemplateVariable("message", "   heLLo world\n  and goodbye\twoRld");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("   Hello World\n  And Goodbye\tWorld"));
            }
        }

        [TestFixture]
        public class When_processing_a_chop_linebreak_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_linebreak_is_chopped()
            {
                SetTemplateContent("${message?chop_linebreak}");
                AddTemplateVariable("message", "Hello\nworld\n");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello\nworld"));
            }
        }

        [TestFixture]
        public class When_processing_a_contains_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_true_is_returned_when_the_string_contains_the_specified_string()
            {
                SetTemplateContent("${message?contains('lo')?string}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_false_is_returned_when_the_string_does_not_contain_the_specified_string()
            {
                SetTemplateContent("${message?contains('bogus')?string}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }
        }

        [TestFixture]
        public class When_processing_a_ends_with_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_true_is_returned_when_the_string_ends_with_the_specified_string()
            {
                SetTemplateContent("${message?ends_with('world')?string}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_false_is_returned_when_the_string_does_not_end_with_the_specified_string()
            {
                SetTemplateContent("${message?ends_with('bogus')?string}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }
        }

        [TestFixture]
        public class When_processing_a_html_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_ampersand_character_is_replaced_with_entity_reference()
            {
                SetTemplateContent("${message?html}");
                AddTemplateVariable("message", "&Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("&amp;Hello"));
            }

            [Test]
            public void Assure_the_greater_than_character_is_replaced_with_entity_reference()
            {
                SetTemplateContent("${message?html}");
                AddTemplateVariable("message", ">Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("&gt;Hello"));
            }

            [Test]
            public void Assure_the_less_than_character_is_replaced_with_entity_reference()
            {
                SetTemplateContent("${message?html}");
                AddTemplateVariable("message", "<Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("&lt;Hello"));
            }

            [Test]
            public void Assure_the_quotation_mark_character_is_replaced_with_entity_reference()
            {
                SetTemplateContent("${message?html}");
                AddTemplateVariable("message", "\"Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("&quot;Hello"));
            }
        }

        [TestFixture]
        public class When_processing_a_index_of_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_index_is_returned_when_the_substring_exists()
            {
                SetTemplateContent("${value?index_of('world')}");
                AddTemplateVariable("value", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("6"));
            }

            [Test]
            public void Assure_the_second_index_is_returned_when_the_start_index_is_larger_than_the_first_index()
            {
                SetTemplateContent("${value?index_of('Hello', 1)}");
                AddTemplateVariable("value", "Hello world! Hello!");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("13"));
            }

            [Test]
            public void Assure_minus_one_is_returned_when_the_substring_does_not_exists()
            {
                SetTemplateContent("${value?index_of('bogus')}");
                AddTemplateVariable("value", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-1"));
            }
        }

        [TestFixture]
        public class When_processing_a_last_index_of_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_index_is_returned_when_the_substring_exists()
            {
                SetTemplateContent("${value?last_index_of('world')}");
                AddTemplateVariable("value", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("6"));
            }

            [Test]
            public void Assure_the_first_index_is_returned_when_the_start_index_is_smaller_than_the_second_index()
            {
                SetTemplateContent("${value?last_index_of('Hello', 10)}");
                AddTemplateVariable("value", "Hello world! Hello!");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("0"));
            }

            [Test]
            public void Assure_minus_one_is_returned_when_the_substring_does_not_exists()
            {
                SetTemplateContent("${value?last_index_of('bogus')}");
                AddTemplateVariable("value", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-1"));
            }
        }

        [TestFixture]
        public class When_processing_a_left_pad_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_string_is_padded_with_spaces_when_single_argument_is_given()
            {
                SetTemplateContent("${message?left_pad(10)}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("     hello"));
            }

            [Test]
            public void Assure_the_string_is_unchanged_when_the_single_argument_is_less_than_the_string_length()
            {
                SetTemplateContent("${message?left_pad(3)}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("hello"));
            }

            [Test]
            public void Assure_the_string_is_padded_with_the_specified_one_character_string()
            {
                SetTemplateContent("${message?left_pad(10, '-')}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-----hello"));
            }

            [Test]
            public void Assure_the_string_is_padded_with_the_specified_three_character_string()
            {
                SetTemplateContent("${message?left_pad(10, '.oO')}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(".oO.ohello"));
            }
        }

        [TestFixture]
        public class When_processing_a_length_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_length_of_the_string_is_returned()
            {
                SetTemplateContent("${message?length}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("5"));
            }
        }

        [TestFixture]
        public class When_processing_a_lower_case_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_all_letters_are_converted_to_lower_case()
            {
                SetTemplateContent("${message?lower_case}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("hello world"));
            }
        }

        [TestFixture]
        public class When_processing_a_matches_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_a_string_matches_the_same_string()
            {
                SetTemplateContent("${message?matches('abc')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_a_string_does_not_match_another_string()
            {
                SetTemplateContent("${message?matches('def')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }

            [Test]
            public void Assure_case_insensitive_matching_is_used_when_the_i_flag_is_set()
            {
                SetTemplateContent("${message?matches('aBC', 'i')?string}");
                AddTemplateVariable("message", "AbC");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_case_sensitive_matching_is_used_when_the_i_flag_is_not_set()
            {
                SetTemplateContent("${message?matches('aBC')?string}");
                AddTemplateVariable("message", "AbC");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }

            [Test]
            public void Assure_multi_line_mode_is_used_when_the_m_flag_is_set()
            {
                SetTemplateContent("${message?matches('^def$', 'm')?string}");
                AddTemplateVariable("message", "abc\ndef");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_multi_line_mode_is_not_used_when_the_m_flag_is_not_set()
            {
                SetTemplateContent("${message?matches('^def$')?string}");
                AddTemplateVariable("message", "abc\ndef");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }

            [Test]
            public void Assure_single_line_mode_is_used_when_the_m_flag_is_not_set()
            {
                SetTemplateContent("${message?matches('^abc$')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_comments_are_allowed_in_regex_when_the_c_flag_is_set()
            {
                SetTemplateContent("${message?matches('abc # a comment', 'c')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_comments_are_not_allowed_in_regex_when_the_c_flag_is_not_set()
            {
                SetTemplateContent("${message?matches('abc # a comment')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }

            [Test]
            public void Assure_whitespaces_are_allowed_in_regex_when_the_c_flag_is_set()
            {
                SetTemplateContent("${message?matches('  ab   c ', 'c')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_whitespaces_are_not_allowed_in_regex_when_the_c_flag_is_not_set()
            {
                SetTemplateContent("${message?matches('  ab   c ')?string}");
                AddTemplateVariable("message", "abc");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }

            [Test]
            public void Assure_a_TemplateException_is_thrown_when_applied_to_a_non_string_value()
            {
                SetTemplateContent("${value?matches('abc')?string}");
                AddTemplateVariable("value", 1);

                Assert.That(
                    () => ProcessTemplate(),
                    Throws.Exception.TypeOf<TemplateException>().
                        With.Message.ContainsSubstring("Expected string"));
            }

            [Test]
            public void Assure_a_TemplateModelException_is_thrown_when_no_parameters_are_given()
            {
                SetTemplateContent("${message?matches()?string}");
                AddTemplateVariable("message", "");

                Assert.That(
                    () => ProcessTemplate(),
                    Throws.Exception.TypeOf<TemplateModelException>().
                        With.Message.ContainsSubstring("Expecting at least one argument"));
            }

            [Test]
            public void Assure_a_TemplateModelException_is_thrown_when_more_than_two_parameters_are_given()
            {
                SetTemplateContent("${message?matches('', '', '')?string}");
                AddTemplateVariable("message", "");

                Assert.That(
                    () => ProcessTemplate(),
                    Throws.Exception.TypeOf<TemplateModelException>().
                        With.Message.ContainsSubstring("Expecting at most two arguments"));
            }

            [Test]
            public void Assure_a_TemplateModelException_is_thrown_when_the_regex_is_illegal()
            {
                SetTemplateContent("${message?matches('[')?string}");
                AddTemplateVariable("message", "");

                Assert.That(
                    () => ProcessTemplate(),
                    Throws.Exception.TypeOf<TemplateModelException>());
            }

            [Test]
            public void Assure_a_TemplateModelException_is_thrown_when_the_regex_is_illegal_and_the_flag_is_set()
            {
                SetTemplateContent("${message?matches('[', 'i')?string}");
                AddTemplateVariable("message", "");

                Assert.That(
                    () => ProcessTemplate(),
                    Throws.Exception.TypeOf<TemplateModelException>());
            }
        }

        [TestFixture]
        public class When_processing_a_replace_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_nothing_happens_when_the_word_does_not_exist()
            {
                SetTemplateContent("${message?replace('bogus', 'everyone')}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello world"));
            }

            [Test]
            public void Assure_the_word_is_replaced_when_the_word_occurs_once()
            {
                SetTemplateContent("${message?replace('world', 'everyone')}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello everyone"));
            }

            [Test]
            public void Assure_both_words_are_replaced_when_the_word_occurs_twice()
            {
                SetTemplateContent("${message?replace('world', 'everyone')}");
                AddTemplateVariable("message", "Hello world, world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello everyone, everyone"));
            }

            [Test]
            public void Assure_the_new_word_is_inserted_between_every_letter_if_the_word_is_empty()
            {
                SetTemplateContent("${message?replace('', '-')}");
                AddTemplateVariable("message", "Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-H-e-l-l-o-"));
            }
        }

        [TestFixture]
        public class When_processing_a_right_pad_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_string_is_padded_with_spaces_when_single_argument_is_given()
            {
                SetTemplateContent("${message?right_pad(10)}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("hello     "));
            }

            [Test]
            public void Assure_the_string_is_unchanged_when_the_single_argument_is_less_than_the_string_length()
            {
                SetTemplateContent("${message?right_pad(3)}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("hello"));
            }

            [Test]
            public void Assure_the_string_is_padded_with_the_specified_one_character_string()
            {
                SetTemplateContent("${message?right_pad(10, '-')}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("hello-----"));
            }

            [Test]
            public void Assure_the_string_is_padded_with_the_specified_three_character_string()
            {
                SetTemplateContent("${message?right_pad(10, '.oO')}");
                AddTemplateVariable("message", "hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("helloO.oO."));
            }
        }

        [TestFixture]
        public class When_processing_a_rtf_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_backslash_character_is_escaped()
            {
                SetTemplateContent("${message?rtf}");
                AddTemplateVariable("message", @"\Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(@"\\Hello"));
            }

            [Test]
            public void Assure_the_left_curly_bracket_character_is_escaped()
            {
                SetTemplateContent("${message?rtf}");
                AddTemplateVariable("message", "{Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(@"\{Hello"));
            }

            [Test]
            public void Assure_the_right_curly_bracket_character_is_escaped()
            {
                SetTemplateContent("${message?rtf}");
                AddTemplateVariable("message", "}Hello");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(@"\}Hello"));
            }
        }

        [TestFixture]
        public class When_processing_a_split_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_a_string_without_the_split_string_is_untouched()
            {
                SetTemplateContent(
                    "<#list 'Hello world'?split(',') as x>" +
                    "${x} -" +
                    "</#list>");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello world -"));
            }

            [Test]
            public void Assure_a_string_with_the_split_string_is_split_into_a_sequence_of_strings()
            {
                SetTemplateContent(
                    "<#list 'some,,test,text,'?split(',') as x>" +
                    "${x} -" +
                    "</#list>");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("some - -test -text - -"));
            }
        }

        [TestFixture]
        public class When_processing_a_starts_with_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_true_is_returned_when_the_string_starts_with_the_specified_string()
            {
                SetTemplateContent("${message?starts_with('Hello')?string}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_false_is_returned_when_the_string_does_not_start_with_the_specified_string()
            {
                SetTemplateContent("${message?starts_with('bogus')?string}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }
        }

        [TestFixture]
        public class When_processing_a_substring_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_one_argument_builtin_works()
            {
                SetTemplateContent("${message?substring(6)}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("world"));
            }

            [Test]
            public void Assure_two_argument_builtin_works()
            {
                SetTemplateContent("${message?substring(1, 5)}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("ello"));
            }
        }

        [TestFixture]
        public class When_processing_a_trim_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_leading_and_trailing_whitespaces_are_removed()
            {
                SetTemplateContent("${message?trim}");
                AddTemplateVariable("message", "    Hello world   ");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello world"));
            }
        }

        [TestFixture]
        public class When_processing_a_uncap_first_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_first_letter_is_changed_to_lower_case()
            {
                SetTemplateContent("${message?uncap_first}");
                AddTemplateVariable("message", "   Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("   hello world"));
            }
        }

        [TestFixture]
        public class When_processing_a_upper_case_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_all_letters_are_converted_to_upper_case()
            {
                SetTemplateContent("${message?upper_case}");
                AddTemplateVariable("message", "Hello world");

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("HELLO WORLD"));
            }
        }
    }
}
// ReSharper restore InconsistentNaming
